#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/jiffies.h>
#include <linux/hrtimer.h>

#define HRTIMER_MODULE_STR_LEN 128

typedef struct {
    int major;
    char module_name[HRTIMER_MODULE_STR_LEN];
    struct hrtimer hrtimer;
    unsigned long long startTime;
    unsigned long long currentTIme;
    ktime_t interval;
} hrtimer_module_data;

static hrtimer_module_data g_private_data = {
    .module_name = "hrtimer_module",
};

/*
 * Return values for the callback function

enum hrtimer_restart {
	HRTIMER_NORESTART,	// Timer is not restarted 
	HRTIMER_RESTART,	// Timer must be restarted 
};
 */
enum hrtimer_restart hrtimer_module_callback(struct hrtimer *timer)
{    
    g_private_data.currentTIme = ktime_to_us(ktime_get());
    printk("[%s][%d] time elaspe %llu\n", __func__, __LINE__, g_private_data.currentTIme - g_private_data.startTime);
    hrtimer_forward_now(timer, g_private_data.interval);
    g_private_data.startTime = g_private_data.currentTIme;
    return HRTIMER_RESTART;
}


static ssize_t hrtimer_module_read(struct file *file, char __user *data, size_t len, loff_t *offset)
{
    printk("[%s][%d]\n", __func__, __LINE__);
    return 0;
}

static ssize_t hrtimer_module_write(struct file *file, const char __user *data, size_t len, loff_t *offset)
{
    printk("[%s][%d]\n", __func__, __LINE__);
    return 0;
}

static const struct file_operations fops = {
    .read = hrtimer_module_read,
    .write = hrtimer_module_write,
};

static int __init hrtimer_module_init(void)
{
    printk("[%s][%d]register char device\n", __func__, __LINE__);
    g_private_data.major = register_chrdev(0, g_private_data.module_name, &fops);
    if (!g_private_data.major) {
        printk(KERN_ERR "register_chrdev failed\n");
        return -ENODEV;
    } else {
        printk("[hrtimer_module]:%s major:%d\n", g_private_data.module_name, g_private_data.major);
    }
    /*
 * The IDs of the various system clocks (for POSIX.1b interval timers):

#define CLOCK_REALTIME			0
#define CLOCK_MONOTONIC			1
#define CLOCK_PROCESS_CPUTIME_ID	2
#define CLOCK_THREAD_CPUTIME_ID		3
#define CLOCK_MONOTONIC_RAW		4
#define CLOCK_REALTIME_COARSE		5
#define CLOCK_MONOTONIC_COARSE		6
#define CLOCK_BOOTTIME			7
#define CLOCK_REALTIME_ALARM		8
#define CLOCK_BOOTTIME_ALARM		9 
*/
/*
 * The driver implementing this got removed. The clock ID is kept as a
 * place holder. Do not reuse!

#define CLOCK_SGI_CYCLE			10
#define CLOCK_TAI			11

#define MAX_CLOCKS			16
#define CLOCKS_MASK			(CLOCK_REALTIME | CLOCK_MONOTONIC)
#define CLOCKS_MONO			CLOCK_MONOTONIC
 */
/*
 * Mode arguments of xxx_hrtimer functions:
 *
 * HRTIMER_MODE_ABS		- Time value is absolute
 * HRTIMER_MODE_REL		- Time value is relative to now
 * HRTIMER_MODE_PINNED		- Timer is bound to CPU (is only considered
 *				  when starting the timer)
 * HRTIMER_MODE_SOFT		- Timer callback function will be executed in
 *				  soft irq context
 * HRTIMER_MODE_HARD		- Timer callback function will be executed in
 *				  hard irq context even on PREEMPT_RT.

enum hrtimer_mode {
	HRTIMER_MODE_ABS	= 0x00,
	HRTIMER_MODE_REL	= 0x01,
	HRTIMER_MODE_PINNED	= 0x02,
	HRTIMER_MODE_SOFT	= 0x04,
	HRTIMER_MODE_HARD	= 0x08,

	HRTIMER_MODE_ABS_PINNED = HRTIMER_MODE_ABS | HRTIMER_MODE_PINNED,
	HRTIMER_MODE_REL_PINNED = HRTIMER_MODE_REL | HRTIMER_MODE_PINNED,

	HRTIMER_MODE_ABS_SOFT	= HRTIMER_MODE_ABS | HRTIMER_MODE_SOFT,
	HRTIMER_MODE_REL_SOFT	= HRTIMER_MODE_REL | HRTIMER_MODE_SOFT,

	HRTIMER_MODE_ABS_PINNED_SOFT = HRTIMER_MODE_ABS_PINNED | HRTIMER_MODE_SOFT,
	HRTIMER_MODE_REL_PINNED_SOFT = HRTIMER_MODE_REL_PINNED | HRTIMER_MODE_SOFT,

	HRTIMER_MODE_ABS_HARD	= HRTIMER_MODE_ABS | HRTIMER_MODE_HARD,
	HRTIMER_MODE_REL_HARD	= HRTIMER_MODE_REL | HRTIMER_MODE_HARD,

	HRTIMER_MODE_ABS_PINNED_HARD = HRTIMER_MODE_ABS_PINNED | HRTIMER_MODE_HARD,
	HRTIMER_MODE_REL_PINNED_HARD = HRTIMER_MODE_REL_PINNED | HRTIMER_MODE_HARD,
};*/
    hrtimer_setup(&g_private_data.hrtimer, hrtimer_module_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    g_private_data.startTime = ktime_to_us(ktime_get());
    g_private_data.interval = us_to_ktime(50);
    hrtimer_start(&g_private_data.hrtimer, g_private_data.interval, HRTIMER_MODE_REL);
    
    return 0;
}

static void __exit hrtimer_module_exit(void)
{
    hrtimer_cancel(&g_private_data.hrtimer);
    unregister_chrdev(g_private_data.major, g_private_data.module_name);
    printk("[%s][%d]unregister device\n", __func__, __LINE__);
}

module_init(hrtimer_module_init);
module_exit(hrtimer_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yujie Guo");
MODULE_DESCRIPTION("Custom Module for Learning");